﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;
using System.Net;
using System.Xml.Linq;

// Twittan

namespace TwitterDotNet.Core
{
    public static class API
    {
        private static readonly Uri public_timeline_url = new Uri("http://twitter.com/statuses/public_timeline.xml");
        private static readonly Uri user_timeline_url = new Uri("http://twitter.com/statuses/user_timeline.xml");
        private static readonly Uri friend_timeline = new Uri("http://twitter.com/statuses/friends_timeline.xml");
        private static readonly Uri replies = new Uri("http://twitter.com/statuses/replies.xml");
        private static readonly Uri update_url = new Uri("http://twitter.com/statuses/update.xml");

        private static readonly Uri friendships_create = new Uri("http://twitter.com/friendships/create.xml");
        private static readonly Uri friendships_destroy = new Uri("http://twitter.com/friendships/destroy.xml");

        private static readonly Uri friends_url = new Uri("http://api.twitter.com/1/statuses/friends.xml");
        private static readonly Uri followers_url = new Uri("http://api.twitter.com/1/statuses/followers.xml");

        private static readonly Uri mention_url = new Uri("http://api.twitter.com/1/statuses/mentions.xml");
        private static readonly Uri show_url = new Uri("http://api.twitter.com/1/users/show.xml");
        private static readonly Uri search_url = new Uri("http://api.twitter.com/1/statuses/search.xml");

        /// <summary>
        /// 公開ユーザーの発言を取得するメソッド
        /// </summary>
        public static Tweet[] PublicTimeline()
        {
            List<Tweet> statuslist = new List<Tweet>();
            HttpWebRequest req;
            HttpWebResponse res = null;
            TextReader tr = null;
            IEnumerable<XElement> statuses = null;

            try
            {
                req = HttpWebRequest.Create(public_timeline_url) as HttpWebRequest;
                req.Method = "GET";
                res = req.GetResponse() as HttpWebResponse;
                tr = new StreamReader(res.GetResponseStream());
                statuses = XElement.Load(tr).Elements("status");
                foreach (XElement status in statuses)
                {
                    statuslist.Add(Tweet.CreateStatus(status));
                }
            }
            catch(Exception e)
            {
                throw new TwitterException("Error in PublicTimeline", e);
            }
            finally
            {
                if (res != null) res.Close();
                if (tr != null) tr.Dispose();
            }

            return statuslist.ToArray();
        }

        /// <summary>
        /// 自分のステータスを取得するメソッド
        /// </summary>
        /// <param name="account">アカウント</param>
        public static Tweet[] UserTimeline(Account account)
        {
            return UserTimeline(account, account.Username);
        }

        /// <summary>
        /// ある人のステータスを取得するメソッド
        /// </summary>
        /// <param name="account">アカウント</param>
        /// <param name="id">対象のID</param>
        /// <returns></returns>
        public static Tweet[] UserTimeline(Account account, long id)
        {
            if (id == 0) throw new ArgumentException("id is invalid");
            return UserTimeline(account, id.ToString());
        }

        /// <summary>
        /// ある人のステータスを取得するメソッド
        /// </summary>
        /// <param name="account">アカウント</param>
        /// <param name="name">対象のユーザー名</param>
        /// <returns></returns>
        public static Tweet[] UserTimeline(Account account, string name)
        {
            if (account == null) throw new ArgumentNullException("account");
            if (name == null) throw new ArgumentException("name");
            if (name == "") throw new ArgumentException("name is invalid");

            List<Tweet> statuslist = new List<Tweet>();
            UriBuilder uri = new UriBuilder(user_timeline_url);
            HttpWebRequest req;
            HttpWebResponse res = null;
            StreamReader sr = null;
            IEnumerable<XElement> statuses = null;

            try
            {
                uri.Query = "id=" + name;
                req = HttpWebRequest.Create(uri.Uri) as HttpWebRequest;
                req.Method = "GET";
                req.Headers.Add("Authorization: Basic " + System.Convert.ToBase64String(Encoding.ASCII.GetBytes(account.Username + ":" + account.Password)));
                res = req.GetResponse() as HttpWebResponse;
                sr = new StreamReader(res.GetResponseStream());
                statuses = XElement.Load(sr).Elements("status");
                foreach (XElement status in statuses)
                {
                    statuslist.Add(Tweet.CreateStatus(status));
                }
            }
            catch (Exception e)
            {
                throw new TwitterException("Error in UserTimeline", e);
            }
            finally
            {
                if (res != null) res.Close();
                if (sr != null) sr.Dispose();
            }

            return statuslist.ToArray();
        }

        /// <summary>
        /// フレンドのステータスを取得するメソッド
        /// </summary>
        /// <param name="account">アカウント</param>
        /// <returns></returns>
        public static Tweet[] FriendsTimeline(Account account)
        {
            if (account == null) throw new ArgumentNullException("account");
            List<Tweet> statuslist = new List<Tweet>();
            HttpWebRequest req;
            HttpWebResponse res = null;
            TextReader tr = null;
            IEnumerable<XElement> statuses = null;
            try
            {
                req = HttpWebRequest.Create(friend_timeline) as HttpWebRequest;
                req.Method = "GET";
                req.Headers.Add("Authorization: Basic " + System.Convert.ToBase64String(Encoding.ASCII.GetBytes(account.Username + ":" + account.Password)));
                res = req.GetResponse() as HttpWebResponse;
                tr = new StreamReader(res.GetResponseStream());
                statuses = XElement.Load(tr).Elements("status");
                foreach (XElement status in statuses)
                {
                    statuslist.Add(Tweet.CreateStatus(status));
                }
            }
            catch (Exception e)
            {
                throw new TwitterException("Error in FriendsTimeline", e);
            }
            finally
            {
                if (res != null) res.Close();
                if (tr != null) tr.Dispose();
            }

            return statuslist.ToArray();
        }

        /// <summary>
        /// リプライを取得するメソッド
        /// </summary>
        /// <param name="account">アカウント</param>
        /// <returns></returns>
        public static Tweet[] Replies(Account account)
        {
            if (account == null) throw new ArgumentNullException("account");

            List<Tweet> statuslist = new List<Tweet>();
            HttpWebRequest req;
            HttpWebResponse res = null;
            TextReader tr = null;
            IEnumerable<XElement> statuses = null;

            try
            {
                req = HttpWebRequest.Create(replies) as HttpWebRequest;
                req.Method = "GET";
                req.Headers.Add("Authorization: Basic " + System.Convert.ToBase64String(Encoding.ASCII.GetBytes(account.Username + ":" + account.Password)));
                res = req.GetResponse() as HttpWebResponse;
                tr = new StreamReader(res.GetResponseStream());
                statuses = XElement.Load(tr).Elements("status");
                foreach (XElement status in statuses)
                {
                    statuslist.Add(Tweet.CreateStatus(status));
                }
            }
            catch (Exception e)
            {
                throw new TwitterException("Error in FriendsTimeline", e);
            }
            finally
            {
                if (res != null) res.Close();
                if (tr != null) tr.Dispose();
            }

            return statuslist.ToArray();
        }

        /// <summary>
        /// つぶやきを送信するメソッド
        /// </summary>
        /// <param name="account">アカウント</param>
        /// <param name="message">つぶやきの内容</param>
        /// <returns></returns>
        public static Tweet Update(Account account, string message)
        {
            if (account == null) throw new ArgumentNullException("account");
            if (message == null) throw new ArgumentNullException("message");
            if (message == "") throw new ArgumentException("message is invalid");
            if (message.Length > 140) throw new ArgumentException("message is too long");

            List<Tweet> statuslist = new List<Tweet>();
            HttpWebRequest req = null;
            HttpWebResponse res = null;
            StreamReader sr = null;
            IEnumerable<XElement> statuses = null;
            try
            {
                req = HttpWebRequest.Create(new Uri(update_url.ToString() + "?status=" + message)) as HttpWebRequest;
                req.Method = "POST";
                req.Headers.Add("Authorization: Basic " + System.Convert.ToBase64String(Encoding.ASCII.GetBytes(account.Username + ":" + account.Password)));
                res = req.GetResponse() as HttpWebResponse;
                sr = new StreamReader(res.GetResponseStream());
                statuses = XDocument.Load(sr).Elements("status");
                foreach (XElement status in statuses)
                {
                    statuslist.Add(Tweet.CreateStatus(status));
                }
                if (statuslist.Count == 0) throw new Exception("statuslist.Count == 0");
                else if (statuslist.Count != 1) throw new Exception("statuslist.Count != 1");
            }
            catch (Exception e)
            {
                throw new TwitterException("Error in Update", e);
            }
            finally
            {
                if (res != null) res.Close();
                if (sr != null) sr.Dispose();
            }
            return statuslist[0];
        }

        /// <summary>
        /// フォローするメソッド
        /// </summary>
        /// <param name="account">アカウント</param>
        /// <param name="username">フォローする人の名前</param>
        public static void Follow(Account account, string username)
        {
            if (account == null) throw new ArgumentNullException("account");
            if (username == null) throw new ArgumentNullException("username");
            if (username == "") throw new ArgumentException("username is invalid");

            HttpWebRequest req = null;
            HttpWebResponse res = null;
            StreamReader sr = null;
            try
            {
                req = HttpWebRequest.Create(new Uri(friendships_create.ToString() + "?id=" + username)) as HttpWebRequest;
                req.Method = "POST";
                req.Headers.Add("Authorization: Basic " + System.Convert.ToBase64String(Encoding.ASCII.GetBytes(account.Username + ":" + account.Password)));
                res = req.GetResponse() as HttpWebResponse;
                sr = new StreamReader(res.GetResponseStream());
            }
            catch (Exception e)
            {
                throw new TwitterException("Error in Follow", e);
            }
            finally
            {
                if (res != null) res.Close();
                if (sr != null) sr.Dispose();
            }
        }

        /// <summary>
        /// フォローをやめるメソッド
        /// </summary>
        /// <param name="account">アカウント</param>
        /// <param name="username">フォローをやめる人の名前</param>
        public static void Unfollow(Account account, string username)
        {
            if (account == null) throw new ArgumentNullException("account");
            if (username == null) throw new ArgumentNullException("username");
            if (username == "") throw new ArgumentException("username is invalid");
            if (username.Length > 140) throw new ArgumentException("username is too long");

            HttpWebRequest req = null;
            HttpWebResponse res = null;
            StreamReader sr = null;
            try
            {
                req = HttpWebRequest.Create(new Uri(friendships_destroy.ToString() + "?id=" + username)) as HttpWebRequest;
                req.Method = "POST";
                req.Headers.Add("Authorization: Basic " + System.Convert.ToBase64String(Encoding.ASCII.GetBytes(account.Username + ":" + account.Password)));
                res = req.GetResponse() as HttpWebResponse;
                sr = new StreamReader(res.GetResponseStream());
            }
            catch (Exception e)
            {
                throw new TwitterException("Error in Unfollow", e);
            }
            finally
            {
                if (res != null) res.Close();
                if (sr != null) sr.Dispose();
            }
        }

        public static User[] Friends(Account account)
        {
            if (account == null) throw new ArgumentNullException("account");

            List<User> userslist = new List<User>();
            HttpWebRequest req;
            HttpWebResponse res = null;
            TextReader tr = null;
            IEnumerable<XElement> users = null;

            try
            {
                req = HttpWebRequest.Create(friends_url) as HttpWebRequest;
                req.Method = "GET";
                req.Headers.Add("Authorization: Basic " + System.Convert.ToBase64String(Encoding.ASCII.GetBytes(account.Username + ":" + account.Password)));
                res = req.GetResponse() as HttpWebResponse;
                tr = new StreamReader(res.GetResponseStream());
                users = XElement.Load(tr).Elements("user");
                foreach(XElement elm in users)
                {
                    userslist.Add(User.CreateUser(elm));
                }
            }
            catch (Exception e)
            {
                throw new TwitterException("Error in Friends", e);
            }
            finally
            {
                if (res != null) res.Close();
                if (tr != null) tr.Dispose();
            }

            return userslist.ToArray();
        }

        public static User[] Followers(Account account)
        {
            if (account == null) throw new ArgumentNullException("account");

            List<User> userslist = new List<User>();
            HttpWebRequest req;
            HttpWebResponse res = null;
            TextReader tr = null;
            IEnumerable<XElement> users = null;

            try
            {
                req = HttpWebRequest.Create(followers_url) as HttpWebRequest;
                req.Method = "GET";
                req.Headers.Add("Authorization: Basic " + System.Convert.ToBase64String(Encoding.ASCII.GetBytes(account.Username + ":" + account.Password)));
                res = req.GetResponse() as HttpWebResponse;
                tr = new StreamReader(res.GetResponseStream());
                users = XElement.Load(tr).Elements("user");
                foreach (XElement user in users)
                {
                    userslist.Add(User.CreateUser(user));
                }
            }
            catch (Exception e)
            {
                throw new TwitterException("Error in Followers", e);
            }
            finally
            {
                if (res != null) res.Close();
                if (tr != null) tr.Dispose();
            }

            return userslist.ToArray();
        }

        public static Tweet[] Mentions(Account account)
        {
            if (account == null) throw new ArgumentNullException("account");

            List<Tweet> tweetlist = new List<Tweet>();
            HttpWebRequest req;
            HttpWebResponse res = null;
            TextReader tr = null;
            IEnumerable<XElement> statuses = null;

            try
            {
                req = HttpWebRequest.Create(mention_url) as HttpWebRequest;
                req.Method = "GET";
                req.Headers.Add("Authorization: Basic " + System.Convert.ToBase64String(Encoding.ASCII.GetBytes(account.Username + ":" + account.Password)));
                res = req.GetResponse() as HttpWebResponse;
                tr = new StreamReader(res.GetResponseStream());
                statuses = XElement.Load(tr).Elements("status");
                foreach (XElement status in statuses)
                {
                    tweetlist.Add(Tweet.CreateStatus(status));
                }
            }
            catch (Exception e)
            {
                throw new TwitterException("Error in Followers", e);
            }
            finally
            {
                if (res != null) res.Close();
                if (tr != null) tr.Dispose();
            }
            if (0 > tweetlist.Count) throw new Exception("statuslist.Count < 0");
            return tweetlist.ToArray();
        }

        public static User Show(long id)
        {
            List<User> userslist = new List<User>();
            HttpWebRequest req;
            HttpWebResponse res = null;
            TextReader tr = null;
            XElement user = null;

            try
            {
                UriBuilder ub = new UriBuilder(show_url);
                ub.Query += string.Format("id={0}", id);
                req = HttpWebRequest.Create(ub.Uri) as HttpWebRequest;
                req.Method = "GET";
                res = req.GetResponse() as HttpWebResponse;
                tr = new StreamReader(res.GetResponseStream());
                user = XElement.Load(tr);
                userslist.Add(User.CreateUser(user));
            }
            catch (Exception e)
            {
                throw new TwitterException("Error in Show", e);
            }
            finally
            {
                if (res != null) res.Close();
                if (tr != null) tr.Dispose();
            }
            if (userslist.Count != 1) throw new TwitterException("Error in Show Method");

            return userslist[0];
        }


        internal static Tweet[] PublicTimeline(OauthAccount account, Dictionary<string, string> prm)
        {
            if (account == null || account.IsLogin == false) throw new TwitterAccountException("Account is invalid");
            List<Tweet> statuslist = new List<Tweet>();
            IEnumerable<XElement> statuses = null;
            statuses = XElement.Parse(account.Oauth.Get(public_timeline_url.ToString(), prm)).Elements("status");
            foreach (XElement status in statuses)
            {
                statuslist.Add(Tweet.CreateStatus(account, status));
            }
            return statuslist.ToArray();
        }

        public static Tweet[] PublicTimeline(OauthAccount account)
        {
            return PublicTimeline(account, (Dictionary<string, string>)null); 
        }

        public static Tweet[] PublicTimeline(OauthAccount account, string name)
        {
            Dictionary<string, string> dic = new Dictionary<string,string>();
            dic.Add("id", name);
            return PublicTimeline(account, dic);
        }


        internal static Tweet[] UserTimeline(OauthAccount account, Dictionary<string, string> prm)
        {
            if (account == null || account.IsLogin == false) throw new TwitterAccountException("Account is invalid");
            
            List<Tweet> statuslist = new List<Tweet>();
            IEnumerable<XElement> statuses = null;
            statuses = XElement.Parse(account.Oauth.Get(user_timeline_url.ToString(), prm)).Elements("status");
            foreach (XElement status in statuses)
            {
                statuslist.Add(Tweet.CreateStatus(account, status));
            }
            return statuslist.ToArray();
        }

        public static Tweet[] UserTimeline(OauthAccount account, string name)
        {
            if(name == null || name == "") throw new ArgumentException("name is invalid");
            Dictionary<string, string> prm = new Dictionary<string, string>();
            prm.Add("id", name);
            return UserTimeline(account, prm);
        }

        /// <summary>
        /// ある人のステータスを取得するメソッド
        /// </summary>
        /// <param name="account">アカウント</param>
        /// <param name="id">対象のID</param>
        /// <returns></returns>
        public static Tweet[] UserTimeline(OauthAccount account, long id)
        {
            if (id == 0) throw new ArgumentException("id is invalid");
            Dictionary<string, string> prm = new Dictionary<string, string>();
            prm.Add("id", id.ToString());
            return UserTimeline(account, prm);
        }

        internal static Tweet[] FriendsTimeline(OauthAccount account, Dictionary<string, string> prm)
        {
            if (account == null || account.IsLogin == false) throw new TwitterAccountException("Account is invalid");

            List<Tweet> statuslist = new List<Tweet>();
            IEnumerable<XElement> statuses = null;
            statuses = XElement.Parse(account.Oauth.Get(friend_timeline.ToString(), prm)).Elements("status");
            foreach (XElement status in statuses)
            {
                statuslist.Add(Tweet.CreateStatus(account, status));
            }
            return statuslist.ToArray();
        }

        public static Tweet[] FriendsTimeline(OauthAccount account)
        {
            return FriendsTimeline(account, null);
        }

        public static Tweet[] FriendsTimeline(OauthAccount account, long since)
        {
            Dictionary<string, string> dic = new Dictionary<string, string>();
            dic.Add("since_id", since.ToString());
            return FriendsTimeline(account, dic);
            //TwitterDotNet.Core.
        }

        internal static Tweet[] Replies(OauthAccount account, Dictionary<string, string> prm)
        {
            if (account == null || account.IsLogin == false) throw new TwitterAccountException("Account is invalid");

            List<Tweet> statuslist = new List<Tweet>();
            IEnumerable<XElement> statuses = null;
            statuses = XElement.Parse(account.Oauth.Get(friend_timeline.ToString(), prm)).Elements("status");
            foreach (XElement status in statuses)
            {
                statuslist.Add(Tweet.CreateStatus(account, status));
            }
            return statuslist.ToArray();
        }

        public static Tweet[] Replies(OauthAccount account)
        {
            return Replies(account, null);
        }

        internal static void Update(OauthAccount account, Dictionary<string, string> prm)
        {
            List<Tweet> statuslist = new List<Tweet>();
            IEnumerable<XElement> statuses = null;
            statuses = XElement.Parse(account.Oauth.Post(update_url.ToString(), prm)).Elements("status");
        }

        public static void Update(OauthAccount account, string message)
        {
            Dictionary<string, string> prm = new Dictionary<string, string>();
            prm.Add("status", account.Oauth.UrlEncode(message));
            Update(account, prm);
        }

        internal static Tweet[] Mentions(OauthAccount account, Dictionary<string, string> prm)
        {
            if (account == null || account.IsLogin == false) throw new TwitterAccountException("Account is invalid");

            List<Tweet> statuslist = new List<Tweet>();
            IEnumerable<XElement> statuses = null;
            statuses = XElement.Parse(account.Oauth.Get(mention_url.ToString(), prm)).Elements("status");
            foreach (XElement status in statuses)
            {
                statuslist.Add(Tweet.CreateStatus(account, status));
            }
            return statuslist.ToArray();
        }

        public static Tweet[] Mentions(OauthAccount account)
        {
            return Mentions(account, null);
        }

        internal static User[] Friends(OauthAccount account, Dictionary<string, string> prm)
        {
            if (account == null || account.IsLogin == false) throw new TwitterAccountException("Account is invalid");

            List<User> statuslist = new List<User>();
            IEnumerable<XElement> users = null;
            users = XElement.Parse(account.Oauth.Get(friends_url.ToString(), prm)).Elements("user");
            foreach (XElement user in users)
            {
                statuslist.Add(User.CreateUser(account, user));
            }
            return statuslist.ToArray();
        }

        public static User[] Friends(OauthAccount account)
        {
            return Friends(account, null);
        }

        internal static User[] Followers(OauthAccount account, Dictionary<string, string> prm)
        {
            if (account == null || account.IsLogin == false) throw new TwitterAccountException("Account is invalid");

            List<User> statuslist = new List<User>();
            IEnumerable<XElement> users = null;
            users = XElement.Parse(account.Oauth.Get(followers_url.ToString(), prm)).Elements("user");
            foreach (XElement user in users)
            {
                statuslist.Add(User.CreateUser(account, user));
            }
            return statuslist.ToArray();
        }

        public static User[] Followers(OauthAccount account)
        {
            return Followers(account, null);
        }

        internal static User Show(OauthAccount account, Dictionary<string, string> prm)
        {
            if (account == null || account.IsLogin == false) throw new TwitterAccountException("Account is invalid");

            List<User> userslist = new List<User>();
            XElement user = XElement.Parse(account.Oauth.Get(show_url.ToString(), prm));
            return User.CreateUser(account, user);
        }

        public static User Show(OauthAccount account, long id)
        {
            Dictionary<string, string> prm = new Dictionary<string, string>();
            prm.Add("id", id.ToString());
            return Show(account, prm);
        }

        internal static Tweet[] Search(OauthAccount account, Dictionary<string, string> prm)
        {
            if (account == null || account.IsLogin == false) throw new TwitterAccountException("Account is invalid");

            List<Tweet> tweetlist = new List<Tweet>();
            IEnumerable<XElement> tweets = null;
            tweets = XElement.Parse(account.Oauth.Get(followers_url.ToString(), prm)).Elements("status");
            foreach (XElement user in tweets)
            {
                tweetlist.Add(Tweet.CreateStatus(account, user));
            }
            return tweetlist.ToArray();
        }

        public static Tweet[] Search(OauthAccount account, string query)
        {
            Dictionary<string, string> prm = new Dictionary<string, string>();
            prm.Add("q", query);
            prm.Add("page", "1");
            return Search(account, prm);
        }
    }
}
